//SA mpi
//$Id: sa_mpi.h 140 2009-09-03 11:33:57Z Oleg.Bulychov $
#pragma once
#ifndef SA_MPI_H
#define SA_MPI_H

#include "src/heo/include/easy_mpi.h"
#include "sa_common.h"
//__________________________________________________________________________________
/**
The Multistart Model with Ring Cooperation
*/
template<class P, class S, class C>
class SA_MPI:
    public SA<P, S, C>
{
public:
    //:gcc bug workaround: 'using SA<P, S, C>::TProblem' fails
    typedef P TProblem;
    typedef S TSolution;
    typedef C TConfig;
    typedef typename S::TDirection TDirection;

    static void solve(TProblem const& problem, TSolution& sol, TConfig const& cfg,
        mpi::communicator& comm)
    {
        int rank = comm.rank();
        int np   = comm.size();
        int prev = (np + rank - 1) % np;
        int next = (rank + 1)      % np;

        uniform_random rnd(0);

        TSolution tmp;
        tmp.init(problem, cfg);

        for (int N = 1; N <= cfg.run_count_; ++N)
        {
            SA_MPI<TProblem, TSolution, TConfig> sa;
            sa.init(problem, cfg);
            sa.generate_Random_Solution(problem, cfg, rnd);
            sa.set_Initial_Temperature(problem, tmp, cfg, rnd, rank);
            sa.update_Solution(sol, cfg, 0, rank);
            for (int step = 1; step <= cfg.max_step_; ++step)
            {
                if (!ring_cooperation<mpi::recv, mpi::send, mpi::isend>::cooperate(sa,
                        rank, prev, next, np, tmp, cfg, step, comm))
                {
                    sa.move(problem, tmp, cfg, rnd);
                }
                if (sa.is_Acceptable(tmp, rnd))
                {
                    sa.accept(tmp);
                    sa.update_Solution(sol, cfg, step, rank);
                }
                sa.update_Temperature(cfg, step, rank);
            }
            sa.clear_MPI(rank, prev, tmp, cfg, comm);
            if (0 == rank)
            {
                std::cout << "0: sol = " << sol.get_Cost() << lf;
                for (int i = 1; i < np; ++i)
                {
                    //__asm { int 3 };
                    mpi::recv(i, comm) >> tmp;
                    if (tmp.get_Cost() > sol.get_Cost())
                    {//:better solution
                        std::cout << i << ":*sol = " << tmp.get_Cost() << lf;
                        sol.assign(tmp);
                    }
                    else
                    {
                        std::cout << i << ": sol = " << tmp.get_Cost() << lf;
                    }
                }
            }
            else
            {
                mpi::send(0, comm) << sol;
            }
        }
    }
private:
    void clear_MPI(int rank, int prev, TSolution& tmp, TConfig const& cfg,
        mpi::communicator& comm)
    {
        comm.barrier();
        if (!cfg.async_mode_)
            return;
        //std::cout << rank << ":enter clear_MPI(...)" << std::endl;
        mpi::recv in(prev, comm);
        while (in.is_Data_Available(0))
        {
            //std::cout << rank << ":***zombi-migrant killed" << std::endl;
            in >> tmp;
        }
        comm.barrier();
        //std::cout << rank << ":leave clear_MPI(...)" << std::endl;
    }
};
//__________________________________________________________________________________
//__________________________________________________________________________________

#endif
//EOF!
